package com.boful.convert.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

/***
 * mediainfo工具封装类
 * 
 * @author ligson
 * 
 */
public class MediaInfo {

	private static Logger logger = Logger.getLogger(MediaInfo.class);

	private String fileName;
	private long fileSize;
	private static final String MAGIC_SPLIT_STRING = "boful_split_character";
	private static final String LINE_SPLIT_STRING = "\n\r";
	private VideoInfo videoInfo = new VideoInfo();
	private AudioInfo audioInfo = new AudioInfo();
	private ImageInfo imageInfo = new ImageInfo();
	private String formatInfo;

	public class VideoInfo {
		private String format;
		private String codecID;
		private String codecIDInfo;
		// Kbps
		private int bitRate;
		private int width;
		private int height;
		private String displayAspectRatio;
		// fps
		private double frameRate;
		// second
		private long duration;
		private int overallBitRate;

		public String getFormat() {
			return format;
		}

		public void setFormat(String format) {
			this.format = format;
		}

		public String getCodecID() {
			return codecID;
		}

		public void setCodecID(String codecID) {
			this.codecID = codecID;
		}

		public String getCodecIDInfo() {
			return codecIDInfo;
		}

		public void setCodecIDInfo(String codecIDInfo) {
			this.codecIDInfo = codecIDInfo;
		}

		public int getBitRate() {
			return bitRate;
		}

		public void setBitRate(int bitRate) {
			this.bitRate = bitRate;
		}

		public int getWidth() {
			return width;
		}

		public void setWidth(int width) {
			this.width = width;
		}

		public int getHeight() {
			return height;
		}

		public void setHeight(int height) {
			this.height = height;
		}

		public String getDisplayAspectRatio() {
			return displayAspectRatio;
		}

		public void setDisplayAspectRatio(String displayAspectRatio) {
			this.displayAspectRatio = displayAspectRatio;
		}

		public double getFrameRate() {
			return frameRate;
		}

		public void setFrameRate(double frameRate) {
			this.frameRate = frameRate;
		}

		public long getDuration() {
			return duration;
		}

		public void setDuration(long duration) {
			this.duration = duration;
		}

		public int getOverallBitRate() {
			return overallBitRate;
		}

		public void setOverallBitRate(int overallBitRate) {
			this.overallBitRate = overallBitRate;
		}

		@Override
		public String toString() {
			return "VideoInfo [format=" + format + ", codecID=" + codecID
					+ ", codecIDInfo=" + codecIDInfo + ", bitRate=" + bitRate
					+ ", width=" + width + ", height=" + height
					+ ", displayAspectRatio=" + displayAspectRatio
					+ ", frameRate=" + frameRate + ", duration=" + duration
					+ ", overallBitRate=" + overallBitRate + "]";
		}

	}

	public class AudioInfo {
		private long duration;
		private String format;
		private String formatInfo;
		private String formatProfile;
		private String codecID;
		private String codecIDInfo;
		private int bitRate;
		private int channel;
		private double samplingRate;
		private int bitDepth;
		private String compressionMode;

		public long getDuration() {
			return duration;
		}

		public void setDuration(long duration) {
			this.duration = duration;
		}

		public String getFormat() {
			return format;
		}

		public void setFormat(String format) {
			this.format = format;
		}

		public String getFormatInfo() {
			return formatInfo;
		}

		public void setFormatInfo(String formatInfo) {
			this.formatInfo = formatInfo;
		}

		public String getFormatProfile() {
			return formatProfile;
		}

		public void setFormatProfile(String formatProfile) {
			this.formatProfile = formatProfile;
		}

		public String getCodecID() {
			return codecID;
		}

		public void setCodecID(String codecID) {
			this.codecID = codecID;
		}

		public String getCodecIDInfo() {
			return codecIDInfo;
		}

		public void setCodecIDInfo(String codecIDInfo) {
			this.codecIDInfo = codecIDInfo;
		}

		public int getBitRate() {
			return bitRate;
		}

		public void setBitRate(int bitRate) {
			this.bitRate = bitRate;
		}

		public int getChannel() {
			return channel;
		}

		public void setChannel(int channel) {
			this.channel = channel;
		}

		public double getSamplingRate() {
			return samplingRate;
		}

		public void setSamplingRate(double samplingRate) {
			this.samplingRate = samplingRate;
		}

		public int getBitDepth() {
			return bitDepth;
		}

		public void setBitDepth(int bitDepth) {
			this.bitDepth = bitDepth;
		}

		public String getCompressionMode() {
			return compressionMode;
		}

		public void setCompressionMode(String compressionMode) {
			this.compressionMode = compressionMode;
		}

		@Override
		public String toString() {
			return "AudioInfo [duration=" + duration + ", format=" + format
					+ ", formatInfo=" + formatInfo + ", formatProfile="
					+ formatProfile + ", codecID=" + codecID + ", codecIDInfo="
					+ codecIDInfo + ", bitRate=" + bitRate + ", channel="
					+ channel + ", samplingRate=" + samplingRate
					+ ", bitDepth=" + bitDepth + ", compressionMode="
					+ compressionMode + "]";
		}

	}

	public class ImageInfo {
		private String format;
		private String formatInfo;
		private int width;
		private int height;
		private int bitDepth;
		private String compressionMode;

		// kb
		// private int streamSize;
		public String getFormat() {
			return format;
		}

		public void setFormat(String format) {
			this.format = format;
		}

		public String getFormatInfo() {
			return formatInfo;
		}

		public void setFormatInfo(String formatInfo) {
			this.formatInfo = formatInfo;
		}

		public int getWidth() {
			return width;
		}

		public void setWidth(int width) {
			this.width = width;
		}

		public int getHeight() {
			return height;
		}

		public void setHeight(int height) {
			this.height = height;
		}

		public int getBitDepth() {
			return bitDepth;
		}

		public void setBitDepth(int bitDepth) {
			this.bitDepth = bitDepth;
		}

		public String getCompressionMode() {
			return compressionMode;
		}

		public void setCompressionMode(String compressionMode) {
			this.compressionMode = compressionMode;
		}

	}

	@Override
	public String toString() {
		return "MediaInfo [fileName=" + fileName + ", fileSize=" + fileSize
				+ ", videoInfo=" + videoInfo + ", audioInfo=" + audioInfo
				+ ", imageInfo=" + imageInfo + ", mediaInfoFile="
				+ mediaInfoFile + ", file=" + file + "]";
	}

	public VideoInfo getVideoInfo() {
		return this.videoInfo;
	}

	public AudioInfo getAudioInfo() {
		return this.audioInfo;
	}

	public ImageInfo getImageInfo() {
		return this.imageInfo;
	}

	public String getFormatInfo() {
		return formatInfo;
	}

	public void setFormatInfo(String formatInfo) {
		this.formatInfo = formatInfo;
	}

	private File mediaInfoFile;
	private File file;

	/***
	 * 构造一个mediainfo对象
	 * 
	 * @param mediaInfoFile
	 *            mediainfo文件
	 * @param file
	 *            获取信息的文件对象
	 * @throws Exception
	 *             文件不存在或者解析错误将会抛出异常
	 */
	public MediaInfo(File mediaInfoFile, File file) throws Exception {
		this.mediaInfoFile = mediaInfoFile;
		this.file = file;
		logger.debug("处理文件:" + file.getAbsolutePath());
		parse();

	}

	public void parse() throws Exception {
		Runtime runtime = Runtime.getRuntime();
		String cmd = mediaInfoFile.getAbsolutePath() + "  "
				+ file.getAbsolutePath();
		// logger.debug("cmd:" + cmd);
		Process process = runtime.exec(cmd);
		BufferedReader reader = new BufferedReader(new InputStreamReader(
				process.getInputStream()));
		String line = null;
		StringBuffer buffer = new StringBuffer();
		while ((line = reader.readLine()) != null) {
			logger.debug(line);
			if (line.startsWith("Complete name")) {
				buffer.append("Complete name                            : " + file.getName());
			} else {
				buffer.append(line);
			}
			buffer.append(LINE_SPLIT_STRING);
			if (line.equals("")) {
				buffer.append(MAGIC_SPLIT_STRING);
			}
		}
		reader.close();
		formatInfo = buffer.toString().replaceAll(MAGIC_SPLIT_STRING, "");
		String[] splitArray = buffer.toString().split(MAGIC_SPLIT_STRING);
		for (String string : splitArray) {
			if (string.startsWith("General")) {
				parseGeneral(string);
			} else if (string.startsWith("Video")) {
				parseVideoInfo(string);
				logger.debug(videoInfo);
			} else if (string.startsWith("Audio")) {
				parseAudioInfo(string);
				logger.debug(audioInfo);
			} else if (string.startsWith("Image")) {
				parseImageInfo(string);
				logger.debug(imageInfo);
			}
		}
	}

	private void parseGeneral(String string) {
		String[] splitArray = string.split(LINE_SPLIT_STRING);
		for (String line : splitArray) {
			String[] lineArray = line.split(":");
			if (lineArray[0].startsWith("Complete name")) {
				this.fileName = lineArray[1];
			} else if (lineArray[0].startsWith("File size")) {
				this.fileSize = sizeToBytes(lineArray[1]);
			} else if (lineArray[0].startsWith("Duration")) {
				long second = durationToSecond(lineArray[1]);
				videoInfo.setDuration(second);
				audioInfo.setDuration(second);
			} else if (lineArray[0].startsWith("Overall bit rate")) {
				videoInfo.setOverallBitRate(parseToNum(lineArray[1], "Kbps"));
			}
		}
	}

	private void parseVideoInfo(String string) {
		String[] splitArray = string.split(LINE_SPLIT_STRING);
		for (String line : splitArray) {
			logger.debug(line);
			String[] lineArray = line.split(":");
			if (lineArray[0].startsWith("Format")) {
				videoInfo.setFormat(lineArray[1]);
			} else if (lineArray[0].startsWith("Codec ID")) {
				if (!lineArray[0].contains("Codec ID/Info")) {
					videoInfo.setCodecID(lineArray[1]);
				} else {
					// lineArray[0].startsWith("Codec ID/Info")
					videoInfo.setCodecIDInfo(lineArray[1]);
				}
			} else if (lineArray[0].startsWith("Duration")) {
				videoInfo.setDuration(durationToSecond(lineArray[1]));
			} else if (lineArray[0].startsWith("Bit rate")) {
				videoInfo.setBitRate(parseKbps(lineArray[1]));
			} else if (lineArray[0].startsWith("Width")) {
				videoInfo.setWidth(parsePixels(lineArray[1]));
			} else if (lineArray[0].startsWith("Height")) {
				videoInfo.setHeight(parsePixels(lineArray[1]));
			} else if (lineArray[0].startsWith("Display aspect ratio")) {
				try {
					videoInfo.setDisplayAspectRatio(lineArray[1] + ":"
							+ lineArray[2]);
				} catch (Exception e) {
					videoInfo.setDisplayAspectRatio(lineArray[1]);
				}
			} else if (lineArray[0].startsWith("Frame rate")) {
				videoInfo.setFrameRate(parseToDouble(lineArray[1], "fps"));
			} else if (lineArray[0].startsWith("Bits/(Pixel*Frame)")) {
				// TODO 不知道什么意思暂不处理
			} else if (lineArray[0].startsWith("Stream size")) {
				// TODO 不知道什么意思暂不处理
			}
		}
	}

	private void parseAudioInfo(String string) {
		String[] splitArray = string.split(LINE_SPLIT_STRING);
		for (String line : splitArray) {
			String[] lineArray = line.split(":");
			String first = lineArray[0];
			if (first.startsWith("Format")) {
				if (first.startsWith("Format/Info")) {
					audioInfo.setFormatInfo(lineArray[1]);
				} else if (first.startsWith("Format profile")) {
					audioInfo.setFormatProfile(lineArray[1]);
				} else {
					audioInfo.setFormat(lineArray[1]);
				}

			} else if (first.startsWith("Codec ID")) {
				if (first.startsWith("Codec ID/Info")) {
					audioInfo.setCodecIDInfo(lineArray[1]);
				} else {
					audioInfo.setCodecID(lineArray[1]);
				}
			} else if (first.startsWith("Bit rate")) {
				audioInfo.setBitRate(parseKbps(lineArray[1]));
			} else if (first.startsWith("Channel(s)")) {
				audioInfo.setChannel(parseToNum(lineArray[1], "channels"));
			} else if (first.startsWith("Sampling rate")) {
				audioInfo.setSamplingRate(parseToDouble(lineArray[1], "KHz"));
			} else if (first.startsWith("Bit depth")) {
				audioInfo.setBitDepth(parseToNum(lineArray[1], "bits"));
			} else if (first.startsWith("Compression mode")) {
				audioInfo.setCompressionMode(lineArray[1]);
			}
		}
	}

	private void parseImageInfo(String string) {
		String[] splitArray = string.split(LINE_SPLIT_STRING);
		for (String line : splitArray) {
			String[] lineArray = line.split(":");
			String first = lineArray[0];
			if (first.startsWith("Format")) {
				if (first.startsWith("Format/Info")) {
					imageInfo.setFormatInfo(lineArray[1]);
				} else {
					imageInfo.setFormat(lineArray[1]);
				}

			} else if (first.startsWith("Width")) {
				imageInfo.setWidth(parsePixels(lineArray[1]));
			} else if (first.startsWith("Height")) {
				imageInfo.setHeight(parsePixels(lineArray[1]));
			} else if (first.startsWith("Bit depth")) {
				imageInfo.setBitDepth(parseToNum(lineArray[1], "bits"));
			} else if (first.startsWith("Compression mode")) {
				imageInfo.setCompressionMode(lineArray[1]);
			} else if (first.startsWith("Stream size")) {
				// TODO 没有用处。。。
			}
		}
	}

	private int parseToNum(String string, String unit) {
		string = string.replaceAll(" ", "");
		string = string.replace(unit, "");
		try {
			return Integer.parseInt(string);
		} catch (Exception e) {
			return 0;
		}
	}

	private double parseToDouble(String string, String unit) {
		string = string.replaceAll(" ", "");
		string = string.replace(unit, "");
		try {
			return Double.parseDouble(string);
		} catch (Exception e) {
			return 0.0;
		}
	}

	private int parseKbps(String kbps) {
		return parseToNum(kbps, "Kbps");
	}

	private int parsePixels(String pixels) {
		return parseToNum(pixels, "pixels");
	}

	private long durationToSecond(String duration) {
		int hour = 0;
		int min = 0;
		int second = 0;
		int millsecond = 0;
		duration = duration.replaceAll(" ", "");
		Pattern hourPattern = Pattern.compile("\\d+h");
		Matcher hourMatcher = hourPattern.matcher(duration);

		if (hourMatcher.find()) {
			try {
				hour = Integer.parseInt(hourMatcher.group().replace("h", ""));
			} catch (Exception e) {
			}
		}

		Pattern minPattern = Pattern.compile("\\d+mn");
		Matcher minMatcher = minPattern.matcher(duration);
		if (minMatcher.find()) {
			try {
				min = Integer.parseInt(minMatcher.group().replace("mn", ""));
			} catch (Exception e) {
			}
		}

		Pattern secondPattern = Pattern.compile("\\d+s");
		Matcher secondMatcher = secondPattern.matcher(duration);
		if (secondMatcher.find()) {
			try {
				second = Integer.parseInt(secondMatcher.group()
						.replace("s", ""));
			} catch (Exception e) {
			}
		}

		Pattern minSecondPattern = Pattern.compile("\\d+ms");
		Matcher minSecondMatcher = minSecondPattern.matcher(duration);
		if (minSecondMatcher.find()) {
			try {
				millsecond = Integer.parseInt(minSecondMatcher.group().replace(
						"ms", ""));
			} catch (Exception e) {
			}
		}

		logger.debug(hour + "h" + min + "mn" + second + "s" + millsecond + "ms");
		return hour * 3600 + min * 60 + second;

	}

	private long sizeToBytes(String size) {
		if (size.contains("GiB")) {
			size = size.replaceAll("GiB", "").replaceAll(" ", "");
			size = size.trim();
			double sizeNum = Double.parseDouble(size);
			return (long) (sizeNum * 1024 * 1024 * 1024);
		} else if (size.contains("MiB")) {
			size = size.replaceAll("MiB", "").replaceAll(" ", "");
			size = size.trim();
			double sizeNum = Double.parseDouble(size);
			return (long) (sizeNum * 1024 * 1024);
		} else if (size.contains("KiB")) {
			size = size.replaceAll("KiB", "").replaceAll(" ", "");
			size = size.trim();
			double sizeNum = Double.parseDouble(size);
			return (long) (sizeNum * 1024);
		} else if (size.contains("Bytes")) {
			size = size.replaceAll("Bytes", "").replaceAll(" ", "");
			size = size.trim();
			double sizeNum = Double.parseDouble(size);
			return (long) (sizeNum);
		}
		return 0;
	}

	public static void main(String[] args) throws Exception {
		File media = new File("C:/Windows/System32/MediaInfo.exe");
		File file = new File("G:/Alizee.-.[La.Isla.Bonita].LIVE.rmvb");
		MediaInfo mediaInfo = new MediaInfo(media, file);
		System.out.println(mediaInfo.getFormatInfo());
	}

}
